package cast

import (
	"errors"
	"fmt"
	"io"
	"os"
	"path/filepath"
	"sort"
	"strings"
	"sync"

	"gitee.com/u-language/u-language/ucom/ast"
	"gitee.com/u-language/u-language/ucom/data"
)

type Package struct {
	initstr            string
	packageName        string
	inAutoFreeFunc     []ast.Node
	inAutoFreeFuncInfo []ast.SymbolInfo
	genInstNodes       [][]ast.Node
	//自己导入的包
	importLocal []*Package
	headerFile  data.Slice[ast.CHeaderFile]
	tocs        data.Slice[*UtoC]
	varInit     []ast.ASSIGNInfo
	//并发
	Thread     bool
	isInitFunc bool
	//是否有自动是否块
	ishaveAutoFree bool
	isImported     bool
}

func NewPackage(Thread bool) *Package {
	return &Package{Thread: Thread, headerFile: data.Slice[ast.CHeaderFile]{Thread: Thread}, tocs: data.Slice[*UtoC]{Thread: Thread}}
}

func (p *Package) AddUastSlice(astp *ast.Package, importPackage map[string]*Package) {
	if len(astp.Trees.Data) == 0 { //如果包里面没有抽象语法树
		panic("包里面没有抽象语法树")
	}
	p.packageName, p.ishaveAutoFree, p.varInit, p.headerFile.Data = *astp.PackageName.Load(), astp.IsHaveAutoFree.Load(), astp.VarInitTable_Global.InitOrder(), astp.CHeaderFile.Data
	for _, v := range astp.Trees.Data { //将所有U语言的抽象语法树转换为C语言的抽象语法树
		toc := newUtoC(p.Thread, &p.headerFile, len(astp.InAutoFreeFuncCallName.Data) != 0)
		toc.parserInit(v)
		p.tocs.Add(toc)
		if v.IsInitFunc.Load() {
			p.isInitFunc = true
		}
	}
	sort.Slice(*astp.GetImportLoacl(), func(i, j int) bool {
		return *(*astp.GetImportLoacl())[i].(*ast.Package).PackageName.Load() < *(*astp.GetImportLoacl())[j].(*ast.Package).PackageName.Load()
	})
	generateImport(&p.importLocal, astp.ImportLoacl, importPackage)
	if len(astp.InAutoFreeFuncCallName.Data) != 0 {
		//生成在自动释放块被调用的函数
		p.inAutoFreeFunc = generateInAutoFreeFunc(&astp.InAutoFreeFuncCallName, astp.FindTree, func(file string) *UtoC { return p.findUtoc(file, true) }, astp.Sbt, &p.inAutoFreeFuncInfo, p.packageName)
	}
	p.genInstNodes = genericToC(p.tocs.Data[0], astp.GenInstNodes, p.Thread)
	//建立按行升序的全局变量初始化顺序
	p.varInit = astp.VarInitTable_Global.Data
}

// OupputC 输出C文件到目录
func (p *Package) OupputC(dir string) ([]string, error) {
	if len(p.tocs.Data) == 0 { //如果包里面没有C抽象语法树
		return nil, errors.New("没有C抽象语法树")
	}
	var err error
	cname := filepath.Join(dir, p.packageName) + ".c"
	fd, err := os.Create(cname)
	if err != nil {
		return nil, err
	}
	p.Oupput(fd, p.GenerateheaderFile())
	fd.Close()
	return []string{cname}, nil
	//TODO:移除[]string返回值
}

func (p *Package) Oupput(w io.StringWriter, header string) {
	var buf strings.Builder
	sort.Slice(p.importLocal, func(i, j int) bool { //排序使输出可复现
		return p.importLocal[i].packageName < p.importLocal[j].packageName
	})
	for _, v := range p.importLocal { //生成所有导入包的C代码
		v.C(&buf)
	}
	//写入头文件
	buf.WriteString(header)
	buf.WriteString("\n")
	sort.Slice(p.tocs.Data, func(i, j int) bool { //排序使输出可复现
		return p.tocs.Data[i].FileName < p.tocs.Data[j].FileName
	})
	for _, v := range p.tocs.Data { //生成自己的C代码
		generateCFile(v.Nodes, &buf)
	}
	for _, v := range p.genInstNodes { //生成泛型C代码
		generateCFile(v, &buf)
	}
	buf.WriteString(p.initstr)
	if p.WithAutoFree() { //生成自动释放块代码
		generateCFile(p.inAutoFreeFunc, &buf)
	}
	w.WriteString(buf.String())
}

// findUtoc 寻找自己及依赖中是否有 file 如果Panic为true没有是会panic
func (p *Package) findUtoc(file string, Panic bool) *UtoC {
	for _, v := range p.tocs.Data {
		if v.FileName == file {
			return v
		}
	}
	for _, v := range p.importLocal {
		if ptr := v.findUtoc(file, false); ptr != nil {
			return ptr
		}
	}
	if Panic {
		panic(fmt.Errorf("没有%s", file))
	}
	return nil
}

func (p *Package) GenerateheaderFile() string {
	var buf strings.Builder
	buf.WriteString(includeStr)
	buf.WriteString("#ifndef ")
	buf.WriteString("_")
	buf.WriteString(p.packageName)
	buf.WriteString("_H__ \n")
	buf.WriteString("#define ")
	buf.WriteString("_")
	buf.WriteString(p.packageName)
	buf.WriteString("_H__ \n")
	if p.WithAutoFree() {
		buf.WriteString("#include \"mempool.h\"\n")
	}
	//生成全局声明
	sort.Slice(p.headerFile.Data, func(i int, j int) bool { //排序是为了保证可复现的构建，和类型声明在最前面
		return nodeCmp(i, j, p.headerFile.Data)
	})
	buf.WriteString("//--- 全局声明 ---\n")
	for _, v := range p.headerFile.Data {
		v.N.CDecl(&buf)
		buf.WriteString("\n")
	}
	for _, v := range p.inAutoFreeFuncInfo {
		generateInAutoFreeFuncDecl(v, &buf)
	}
	buf.WriteString("//--- init声明 ---\n")
	//生成init声明
	p.initstr = generateInit(p.packageName, p.isInitFunc, p.varInit, &buf, p.importLocal, p.isImported)
	buf.WriteString("#endif\n")
	return buf.String()
}

func (p *Package) String() string {
	var buf strings.Builder
	for _, v := range p.importLocal { //生成所有导入包的C代码
		v.C(&buf)
	}
	buf.WriteString("--- 头文件 ---\n")
	buf.WriteString(p.GenerateheaderFile())
	var once sync.Once
	for _, v := range p.tocs.Data {
		buf.WriteString("--- ")
		buf.WriteString(v.FileName)
		buf.WriteString(" ---\n")
		generateCFile(v.Nodes, &buf)
		once.Do(func() {
			buf.WriteString(p.initstr)
		})
	}
	if p.WithAutoFree() {
		buf.WriteString("--- ")
		buf.WriteString("inautofreefunc.c")
		buf.WriteString(" ---\n")
		generateCFile(p.inAutoFreeFunc, &buf)
	}
	return buf.String()
}

func (p *Package) C(buf *strings.Builder) {
	p.Oupput(buf, p.GenerateheaderFile())
}

func (p *Package) WithAutoFree() bool {
	return p.ishaveAutoFree
}
